// META: title=test WebNN API layerNormalization operation
// META: global=window,dedicatedworker
// META: variant=?cpu
// META: variant=?gpu
// META: variant=?npu
// META: script=../resources/utils.js
// META: timeout=long

'use strict';

// https://www.w3.org/TR/webnn/#api-mlgraphbuilder-layernorm
// Normalize the input using Layer-Normalization.
//
// dictionary MLLayerNormalizationOptions {
//   MLOperand scale;
//   MLOperand bias;
//   sequence<[EnforceRange] unsigned long> axes;
//   double epsilon = 1e-5;
// };
//
// MLOperand layerNormalization(
//     MLOperand input, optional MLLayerNormalizationOptions options = {});


const getLayerNormPrecisionTolerance = (graphResources) => {
  const toleranceValueDict = {float32: 1 / 1024, float16: 1 / 512};
  const expectedDataType =
      getExpectedDataTypeOfSingleOutput(graphResources.expectedOutputs);
  return {metricType: 'ATOL', value: toleranceValueDict[expectedDataType]};
};

const layerNormTests = [
  {
    'name': 'layerNormalization float32 2D tensor default options',
    'graph': {
      'inputs': {
        'layerNormInput': {
          'data': [
            -35.51446533203125,  54.735408782958984,  19.659019470214844,
            -15.882678031921387, 65.48657989501953,   25.818492889404297,
            97.55302429199219,   -8.057161331176758,  62.9412956237793,
            -48.91555404663086,  91.90644073486328,   46.67098617553711,
            -74.85331726074219,  30.126361846923828,  26.13089370727539,
            59.30270767211914,   -60.361995697021484, 18.55615234375,
            -88.03730773925781,  -26.5667724609375,   70.81292724609375,
            9.105611801147461,   56.66746139526367,   21.78444480895996
          ],
          'descriptor': {'dimensions': [4, 6], 'dataType': 'float32'}
        }
      },
      'operators': [{
        'name': 'layerNormalization',
        'arguments': [{'input': 'layerNormInput'}],
        'outputs': 'layerNormOutput'
      }],
      'expectedOutputs': {
        'layerNormOutput': {
          'data': [
            -1.5257738828659058,  0.997844934463501,   0.017018765211105347,
            -0.9768186211585999,  1.2984753847122192,  0.18925349414348602,
            1.0812907218933105,   -0.915019690990448,  0.4270379841327667,
            -1.6873507499694824,  0.9745554327964783,  0.11948632448911667,
            -1.5086692571640015,  0.6123882532119751,  0.5316619873046875,
            1.2018805742263794,   -1.215880036354065,  0.378618448972702,
            -1.795186161994934,   -0.6376377940177917, 1.1961140632629395,
            0.034106940031051636, 0.9297415614128113,  0.2728613615036011
          ],
          'descriptor': {'dimensions': [4, 6], 'dataType': 'float32'}
        }
      }
    }
  },
  {
    'name': 'layerNormalization float32 3D tensor default options',
    'graph': {
      'inputs': {
        'layerNormInput': {
          'data': [
            -35.51446533203125,  54.735408782958984,  19.659019470214844,
            -15.882678031921387, 65.48657989501953,   25.818492889404297,
            97.55302429199219,   -8.057161331176758,  62.9412956237793,
            -48.91555404663086,  91.90644073486328,   46.67098617553711,
            -74.85331726074219,  30.126361846923828,  26.13089370727539,
            59.30270767211914,   -60.361995697021484, 18.55615234375,
            -88.03730773925781,  -26.5667724609375,   70.81292724609375,
            9.105611801147461,   56.66746139526367,   21.78444480895996
          ],
          'descriptor': {'dimensions': [2, 3, 4], 'dataType': 'float32'}
        }
      },
      'operators': [{
        'name': 'layerNormalization',
        'arguments': [{'input': 'layerNormInput'}],
        'outputs': 'layerNormOutput'
      }],
      'expectedOutputs': {
        'layerNormOutput': {
          'data': [
            -1.4057259559631348, 0.5396455526351929,  -0.21643976867198944,
            -0.9825550317764282, 0.7713912725448608,  -0.08366990834474564,
            1.46259605884552,    -0.8138729333877563, 0.7165266871452332,
            -1.6945916414260864, 1.3408818244934082,  0.3658137917518616,
            -1.5234858989715576, 0.5162702202796936,  0.43863821029663086,
            1.0831668376922607,  -1.2419193983078003, 0.29146093130111694,
            -1.7796510457992554, -0.5852779150009155, 1.3068104982376099,
            0.10783683508634567, 1.0319640636444092,  0.35418668389320374
          ],
          'descriptor': {'dimensions': [2, 3, 4], 'dataType': 'float32'}
        }
      }
    }
  },
  {
    'name': 'layerNormalization float32 4D tensor default options',
    'graph': {
      'inputs': {
        'layerNormInput': {
          'data': [
            -35.51446533203125,  54.735408782958984,  19.659019470214844,
            -15.882678031921387, 65.48657989501953,   25.818492889404297,
            97.55302429199219,   -8.057161331176758,  62.9412956237793,
            -48.91555404663086,  91.90644073486328,   46.67098617553711,
            -74.85331726074219,  30.126361846923828,  26.13089370727539,
            59.30270767211914,   -60.361995697021484, 18.55615234375,
            -88.03730773925781,  -26.5667724609375,   70.81292724609375,
            9.105611801147461,   56.66746139526367,   21.78444480895996
          ],
          'descriptor': {'dimensions': [2, 1, 4, 3], 'dataType': 'float32'}
        }
      },
      'operators': [{
        'name': 'layerNormalization',
        'arguments': [{'input': 'layerNormInput'}],
        'outputs': 'layerNormOutput'
      }],
      'expectedOutputs': {
        'layerNormOutput': {
          'data': [
            -1.4057259559631348, 0.5396455526351929,  -0.21643976867198944,
            -0.9825550317764282, 0.7713912725448608,  -0.08366990834474564,
            1.46259605884552,    -0.8138729333877563, 0.7165266871452332,
            -1.6945916414260864, 1.3408818244934082,  0.3658137917518616,
            -1.5234858989715576, 0.5162702202796936,  0.43863821029663086,
            1.0831668376922607,  -1.2419193983078003, 0.29146093130111694,
            -1.7796510457992554, -0.5852779150009155, 1.3068104982376099,
            0.10783683508634567, 1.0319640636444092,  0.35418668389320374
          ],
          'descriptor': {'dimensions': [2, 1, 4, 3], 'dataType': 'float32'}
        }
      }
    }
  },
  {
    'name': 'layerNormalization float32 5D tensor default options',
    'graph': {
      'inputs': {
        'layerNormInput': {
          'data': [
            -35.51446533203125,  54.735408782958984,  19.659019470214844,
            -15.882678031921387, 65.48657989501953,   25.818492889404297,
            97.55302429199219,   -8.057161331176758,  62.9412956237793,
            -48.91555404663086,  91.90644073486328,   46.67098617553711,
            -74.85331726074219,  30.126361846923828,  26.13089370727539,
            59.30270767211914,   -60.361995697021484, 18.55615234375,
            -88.03730773925781,  -26.5667724609375,   70.81292724609375,
            9.105611801147461,   56.66746139526367,   21.78444480895996
          ],
          'descriptor': {'dimensions': [2, 1, 2, 2, 3], 'dataType': 'float32'}
        }
      },
      'operators': [{
        'name': 'layerNormalization',
        'arguments': [{'input': 'layerNormInput'}],
        'outputs': 'layerNormOutput'
      }],
      'expectedOutputs': {
        'layerNormOutput': {
          'data': [
            -1.4057259559631348, 0.5396455526351929,  -0.21643976867198944,
            -0.9825550317764282, 0.7713912725448608,  -0.08366990834474564,
            1.46259605884552,    -0.8138729333877563, 0.7165266871452332,
            -1.6945916414260864, 1.3408818244934082,  0.3658137917518616,
            -1.5234858989715576, 0.5162702202796936,  0.43863821029663086,
            1.0831668376922607,  -1.2419193983078003, 0.29146093130111694,
            -1.7796510457992554, -0.5852779150009155, 1.3068104982376099,
            0.10783683508634567, 1.0319640636444092,  0.35418668389320374
          ],
          'descriptor': {'dimensions': [2, 1, 2, 2, 3], 'dataType': 'float32'}
        }
      }
    }
  },
  {
    'name': 'layerNormalization float32 4D tensor options.scale',
    'graph': {
      'inputs': {
        'layerNormInput': {
          'data': [
            -35.51446533203125,  54.735408782958984,  19.659019470214844,
            -15.882678031921387, 65.48657989501953,   25.818492889404297,
            97.55302429199219,   -8.057161331176758,  62.9412956237793,
            -48.91555404663086,  91.90644073486328,   46.67098617553711,
            -74.85331726074219,  30.126361846923828,  26.13089370727539,
            59.30270767211914,   -60.361995697021484, 18.55615234375,
            -88.03730773925781,  -26.5667724609375,   70.81292724609375,
            9.105611801147461,   56.66746139526367,   21.78444480895996
          ],
          'descriptor': {'dimensions': [2, 1, 4, 3], 'dataType': 'float32'}
        },
        'layerNormScale': {
          'data': [
            -3.8228423595428467, -5.452458381652832, 0.6776165962219238,
            -4.027037620544434, -3.7771618366241455, -9.327335357666016,
            7.1816911697387695, 1.5054303407669067, 3.120894193649292,
            0.5214731693267822, 2.6719748973846436, -3.571370840072632
          ],
          'descriptor': {'dimensions': [1, 4, 3], 'dataType': 'float32'},
          'constant': true
        }
      },
      'operators': [{
        'name': 'layerNormalization',
        'arguments': [
          {'input': 'layerNormInput'}, {'options': {'scale': 'layerNormScale'}}
        ],
        'outputs': 'layerNormOutput'
      }],
      'expectedOutputs': {
        'layerNormOutput': {
          'data': [
            5.373868465423584,   -2.942394971847534,  -0.14666318893432617,
            3.9567861557006836,  -2.9136698246002197, 0.780417263507843,
            10.503913879394531,  -1.225229024887085,  2.236203908920288,
            -0.8836840987205505, 3.5828025341033936,  -1.3064566850662231,
            5.824046611785889,   -2.814941883087158,  0.29722854495048523,
            -4.3619537353515625, 4.6909308433532715,  -2.7185537815093994,
            -12.780903816223145, -0.8810951709747314, 4.0784173011779785,
            0.05623401328921318, 2.7573819160461426,  -1.2649319171905518
          ],
          'descriptor': {'dimensions': [2, 1, 4, 3], 'dataType': 'float32'}
        }
      }
    }
  },
  {
    'name': 'layerNormalization float32 4D tensor options.bias',
    'graph': {
      'inputs': {
        'layerNormInput': {
          'data': [
            -35.51446533203125,  54.735408782958984,  19.659019470214844,
            -15.882678031921387, 65.48657989501953,   25.818492889404297,
            97.55302429199219,   -8.057161331176758,  62.9412956237793,
            -48.91555404663086,  91.90644073486328,   46.67098617553711,
            -74.85331726074219,  30.126361846923828,  26.13089370727539,
            59.30270767211914,   -60.361995697021484, 18.55615234375,
            -88.03730773925781,  -26.5667724609375,   70.81292724609375,
            9.105611801147461,   56.66746139526367,   21.78444480895996
          ],
          'descriptor': {'dimensions': [2, 1, 4, 3], 'dataType': 'float32'}
        },
        'layerNormBias': {
          'data': [
            7.862982749938965, -3.6603047847747803, -6.955524444580078,
            -6.397322654724121, 3.268958568572998, -2.7498080730438232,
            -4.080942153930664, -7.137991905212402, 8.465653419494629,
            2.762545108795166, 0.8230442404747009, -3.827561378479004
          ],
          'descriptor': {'dimensions': [1, 4, 3], 'dataType': 'float32'},
          'constant': true
        }
      },
      'operators': [{
        'name': 'layerNormalization',
        'arguments': [
          {'input': 'layerNormInput'}, {'options': {'bias': 'layerNormBias'}}
        ],
        'outputs': 'layerNormOutput'
      }],
      'expectedOutputs': {
        'layerNormOutput': {
          'data': [
            6.45725679397583,    -3.120659112930298,  -7.171964168548584,
            -7.37987756729126,   4.040349960327148,   -2.8334779739379883,
            -2.6183459758758545, -7.951864719390869,  9.182180404663086,
            1.0679534673690796,  2.163926124572754,   -3.461747646331787,
            6.339496612548828,   -3.1440346240997314, -6.516886234283447,
            -5.314155578613281,  2.027039051055908,   -2.4583470821380615,
            -5.860593318939209,  -7.723269939422607,  9.77246379852295,
            2.8703818321228027,  1.8550082445144653,  -3.473374605178833
          ],
          'descriptor': {'dimensions': [2, 1, 4, 3], 'dataType': 'float32'}
        }
      }
    }
  },
  {
    'name': 'layerNormalization float32 4D tensor options.axes=[2]',
    'graph': {
      'inputs': {
        'layerNormInput': {
          'data': [
            -35.51446533203125,  54.735408782958984,  19.659019470214844,
            -15.882678031921387, 65.48657989501953,   25.818492889404297,
            97.55302429199219,   -8.057161331176758,  62.9412956237793,
            -48.91555404663086,  91.90644073486328,   46.67098617553711,
            -74.85331726074219,  30.126361846923828,  26.13089370727539,
            59.30270767211914,   -60.361995697021484, 18.55615234375,
            -88.03730773925781,  -26.5667724609375,   70.81292724609375,
            9.105611801147461,   56.66746139526367,   21.78444480895996
          ],
          'descriptor': {'dimensions': [2, 1, 4, 3], 'dataType': 'float32'}
        }
      },
      'operators': [{
        'name': 'layerNormalization',
        'arguments': [{'input': 'layerNormInput'}, {'options': {'axes': [2]}}],
        'outputs': 'layerNormOutput'
      }],
      'expectedOutputs': {
        'layerNormOutput': {
          'data': [
            -0.6012066006660461,  0.10132180899381638, -1.112992763519287,
            -0.26228588819503784, 0.3943416476249695,  -0.7543209195137024,
            1.6960537433624268,   -1.6100702285766602, 1.4073745012283325,
            -0.8325613141059875,  1.114406704902649,   0.45993921160697937,
            -0.8445013165473938,  0.6554933190345764,  -0.3856155574321747,
            1.3668763637542725,   -1.3111618757247925, -0.7422532439231873,
            -1.0618212223052979,  -0.5766634941101074, 1.7181260585784912,
            0.539446234703064,    1.2323321104049683,  -0.5902572274208069
          ],
          'descriptor': {'dimensions': [2, 1, 4, 3], 'dataType': 'float32'}
        }
      }
    }
  },
  {
    'name': 'layerNormalization float32 4D tensor options.epsilon',
    'graph': {
      'inputs': {
        'layerNormInput': {
          'data': [
            -35.51446533203125,  54.735408782958984,  19.659019470214844,
            -15.882678031921387, 65.48657989501953,   25.818492889404297,
            97.55302429199219,   -8.057161331176758,  62.9412956237793,
            -48.91555404663086,  91.90644073486328,   46.67098617553711,
            -74.85331726074219,  30.126361846923828,  26.13089370727539,
            59.30270767211914,   -60.361995697021484, 18.55615234375,
            -88.03730773925781,  -26.5667724609375,   70.81292724609375,
            9.105611801147461,   56.66746139526367,   21.78444480895996
          ],
          'descriptor': {'dimensions': [2, 1, 4, 3], 'dataType': 'float32'}
        }
      },
      'operators': [{
        'name': 'layerNormalization',
        'arguments':
            [{'input': 'layerNormInput'}, {'options': {'epsilon': 0.0001}}],
        'outputs': 'layerNormOutput'
      }],
      'expectedOutputs': {
        'layerNormOutput': {
          'data': [
            -1.4057258367538452, 0.5396455526351929,  -0.21643976867198944,
            -0.9825550317764282, 0.7713912725448608,  -0.08366990089416504,
            1.46259605884552,    -0.8138729333877563, 0.7165266871452332,
            -1.6945916414260864, 1.3408817052841187,  0.3658137619495392,
            -1.5234858989715576, 0.5162702202796936,  0.43863821029663086,
            1.0831668376922607,  -1.2419193983078003, 0.29146093130111694,
            -1.7796509265899658, -0.5852779150009155, 1.3068104982376099,
            0.10783682763576508, 1.0319639444351196,  0.35418668389320374
          ],
          'descriptor': {'dimensions': [2, 1, 4, 3], 'dataType': 'float32'}
        }
      }
    }
  },
  {
    'name':
        'layerNormalization float32 4D tensor options.scale and options.axes=[0, 2]',
    'graph': {
      'inputs': {
        'layerNormInput': {
          'data': [
            -35.51446533203125,  54.735408782958984,  19.659019470214844,
            -15.882678031921387, 65.48657989501953,   25.818492889404297,
            97.55302429199219,   -8.057161331176758,  62.9412956237793,
            -48.91555404663086,  91.90644073486328,   46.67098617553711,
            -74.85331726074219,  30.126361846923828,  26.13089370727539,
            59.30270767211914,   -60.361995697021484, 18.55615234375,
            -88.03730773925781,  -26.5667724609375,   70.81292724609375,
            9.105611801147461,   56.66746139526367,   21.78444480895996
          ],
          'descriptor': {'dimensions': [2, 1, 4, 3], 'dataType': 'float32'}
        },
        'layerNormScale': {
          'data': [
            8.72657299041748, -5.388210773468018, -6.811323165893555,
            4.707905292510986, -4.705780029296875, -5.143046855926514,
            -1.1115549802780151, 5.250569820404053
          ],
          'descriptor': {'dimensions': [2, 4], 'dataType': 'float32'},
          'constant': true
        }
      },
      'operators': [{
        'name': 'layerNormalization',
        'arguments': [
          {'input': 'layerNormInput'},
          {'options': {'scale': 'layerNormScale', 'axes': [0, 2]}}
        ],
        'outputs': 'layerNormOutput'
      }],
      'expectedOutputs': {
        'layerNormOutput': {
          'data': [
            -3.3744184970855713, 5.22746467590332,     -7.580371856689453,
            0.3324689269065857,  -4.414334774017334,   2.973374605178833,
            -12.369945526123047, 4.680946350097656,    -9.247408866882324,
            -2.8648624420166016, 6.40486478805542,     2.4516794681549072,
            4.884079456329346,   -0.44672244787216187, 2.521172285079956,
            -6.083702564239502,  9.044846534729004,    4.759283065795898,
            1.3962621688842773,  1.185346245765686,    -1.959165334701538,
            1.8479242324829102,  3.3530402183532715,   -3.986907958984375
          ],
          'descriptor': {'dimensions': [2, 1, 4, 3], 'dataType': 'float32'}
        }
      }
    }
  },
  {
    'name':
        'layerNormalization float32 4D tensor options.bias and options.axes=[3, 1, 2]',
    'graph': {
      'inputs': {
        'layerNormInput': {
          'data': [
            -35.51446533203125,  54.735408782958984,  19.659019470214844,
            -15.882678031921387, 65.48657989501953,   25.818492889404297,
            97.55302429199219,   -8.057161331176758,  62.9412956237793,
            -48.91555404663086,  91.90644073486328,   46.67098617553711,
            -74.85331726074219,  30.126361846923828,  26.13089370727539,
            59.30270767211914,   -60.361995697021484, 18.55615234375,
            -88.03730773925781,  -26.5667724609375,   70.81292724609375,
            9.105611801147461,   56.66746139526367,   21.78444480895996
          ],
          'descriptor': {'dimensions': [2, 1, 4, 3], 'dataType': 'float32'}
        },
        'layerNormBias': {
          'data': [
            -0.1396923065185547, -6.156772136688232, 4.363296031951904,
            8.8598051071167, 9.772650718688965, -3.4626545906066895,
            9.744950294494629, -0.3958968222141266, -8.497353553771973,
            6.172536849975586, -2.8930461406707764, 1.7220044136047363
          ],
          'descriptor': {'dimensions': [3, 1, 4], 'dataType': 'float32'},
          'constant': true
        }
      },
      'operators': [{
        'name': 'layerNormalization',
        'arguments': [
          {'input': 'layerNormInput'},
          {'options': {'bias': 'layerNormBias', 'axes': [3, 1, 2]}}
        ],
        'outputs': 'layerNormOutput'
      }],
      'expectedOutputs': {
        'layerNormOutput': {
          'data': [
            -1.5454182624816895, 10.312295913696289, -8.713793754577637,
            -7.139327049255371,  -2.691263198852539, 6.088866710662842,
            5.825891971588135,   8.931077003479004,  -2.1765193939208984,
            7.165213584899902,   0.9449849724769592, 2.087818145751953,
            -1.6631782054901123, 10.288921356201172, -8.058714866638184,
            -5.073605060577393,  -4.704574108123779, 6.463997840881348,
            2.5836451053619385,  9.159672737121582,  -1.5862356424331665,
            8.967641830444336,   0.6360672116279602, 2.0761911869049072
          ],
          'descriptor': {'dimensions': [2, 1, 4, 3], 'dataType': 'float32'}
        }
      }
    }
  },
  {
    'name': 'layerNormalization float32 4D tensor all options',
    'graph': {
      'inputs': {
        'layerNormInput': {
          'data': [
            -35.51446533203125,  54.735408782958984,  19.659019470214844,
            -15.882678031921387, 65.48657989501953,   25.818492889404297,
            97.55302429199219,   -8.057161331176758,  62.9412956237793,
            -48.91555404663086,  91.90644073486328,   46.67098617553711,
            -74.85331726074219,  30.126361846923828,  26.13089370727539,
            59.30270767211914,   -60.361995697021484, 18.55615234375,
            -88.03730773925781,  -26.5667724609375,   70.81292724609375,
            9.105611801147461,   56.66746139526367,   21.78444480895996
          ],
          'descriptor': {'dimensions': [2, 1, 4, 3], 'dataType': 'float32'}
        },
        'layerNormScale': {
          'data': [
            7.715926647186279, 1.7371079921722412, 9.13965129852295,
            5.758823394775391, -2.8198351860046387, -0.6866958141326904
          ],
          'descriptor': {'dimensions': [2, 3, 1], 'dataType': 'float32'},
          'constant': true
        },
        'layerNormBias': {
          'data': [
            -8.710672378540039, -7.642981052398682, 4.937538146972656,
            -2.1876745223999023, -4.067612648010254, -6.836254596710205
          ],
          'descriptor': {'dimensions': [2, 3, 1], 'dataType': 'float32'},
          'constant': true
        }
      },
      'operators': [{
        'name': 'layerNormalization',
        'arguments': [
          {'input': 'layerNormInput'}, {
            'options': {
              'scale': 'layerNormScale',
              'bias': 'layerNormBias',
              'axes': [0, 3, 1],
              'epsilon': 0.0001
            }
          }
        ],
        'outputs': 'layerNormOutput'
      }],
      'expectedOutputs': {
        'layerNormOutput': {
          'data': [
            -15.487034797668457, -5.628695964813232,  8.29687786102295,
            -14.294686317443848, -5.639192581176758,  7.11608362197876,
            0.7769554257392883,  -8.346451759338379,  11.279659271240234,
            -22.506288528442383, -5.173816204071045,  8.506545066833496,
            -12.360523223876953, -5.77052116394043,   -7.18900203704834,
            3.6336634159088135,  0.8666883707046509,  -6.884884357452393,
            -11.648612976074219, -2.117840528488159,  -7.396423816680908,
            -4.869131088256836,  -5.8111701011657715, -6.714934349060059
          ],
          'descriptor': {'dimensions': [2, 1, 4, 3], 'dataType': 'float32'}
        }
      }
    }
  }
];

if (navigator.ml) {
  layerNormTests.forEach((test) => {
    webnn_conformance_test(
        buildGraphAndCompute, getLayerNormPrecisionTolerance, test);
  });
} else {
  test(() => assert_implements(navigator.ml, 'missing navigator.ml'));
}
